{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 5.6 深度卷积神经网络（AlexNet）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-19T07:36:45.657048Z",
     "start_time": "2019-03-19T07:36:45.285668Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.0.0-rc1\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "import time\n",
    "import paddle\n",
    "from paddle import nn, optimizer\n",
    "\n",
    "print(paddle.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.6.2 AlexNet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-19T07:36:45.703036Z",
     "start_time": "2019-03-19T07:36:45.658231Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/home/iie/miniconda3/envs/yolov5/lib/python3.8/site-packages/ipykernel/ipkernel.py:287: DeprecationWarning: `should_run_async` will not call `transform_cell` automatically in the future. Please pass the result to `transformed_cell` argument and any exception that happen during thetransform in `preprocessing_exc_tuple` in IPython 7.17 and above.\n",
      "  and should_run_async(code)\n"
     ]
    }
   ],
   "source": [
    "class AlexNet(nn.Layer):\n",
    "    def __init__(self):\n",
    "        super(AlexNet, self).__init__()\n",
    "        self.conv = nn.Sequential(\n",
    "            nn.Conv2D(1, 96, 11, 4), # in_channels, out_channels, kernel_size, stride, padding\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2D(3, 2), # kernel_size, stride\n",
    "            # 减小卷积窗口，使用填充为2来使得输入与输出的高和宽一致，且增大输出通道数\n",
    "            nn.Conv2D(96, 256, 5, 1, 2),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2D(3, 2),\n",
    "            # 连续3个卷积层，且使用更小的卷积窗口。除了最后的卷积层外，进一步增大了输出通道数。\n",
    "            # 前两个卷积层后不使用池化层来减小输入的高和宽\n",
    "            nn.Conv2D(256, 384, 3, 1, 1),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2D(384, 384, 3, 1, 1),\n",
    "            nn.ReLU(),\n",
    "            nn.Conv2D(384, 256, 3, 1, 1),\n",
    "            nn.ReLU(),\n",
    "            nn.MaxPool2D(3, 2)\n",
    "        )\n",
    "         # 这里全连接层的输出个数比LeNet中的大数倍。使用丢弃层来缓解过拟合\n",
    "        self.fc = nn.Sequential(\n",
    "            nn.Linear(256*5*5, 4096),\n",
    "            nn.ReLU(),\n",
    "            nn.Dropout(0.5),\n",
    "            nn.Linear(4096, 4096),\n",
    "            nn.ReLU(),\n",
    "            nn.Dropout(0.5),\n",
    "            # 输出层。由于这里使用Fashion-MNIST，所以用类别数为10，而非论文中的1000\n",
    "            nn.Linear(4096, 10),\n",
    "        )\n",
    "\n",
    "    def forward(self, img):\n",
    "        feature = self.conv(img)\n",
    "        output = self.fc(feature.reshape((img.shape[0], -1)))\n",
    "        return output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-19T07:36:46.053598Z",
     "start_time": "2019-03-19T07:36:45.704356Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.AlexNet object at 0x7fe0ca01b090>\n"
     ]
    }
   ],
   "source": [
    "net = AlexNet()\n",
    "print(net)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.6.3 读取数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-19T07:36:46.066761Z",
     "start_time": "2019-03-19T07:36:46.054928Z"
    }
   },
   "outputs": [],
   "source": [
    "import paddle.vision.datasets as datasets\n",
    "import paddle.vision.transforms as transforms\n",
    "\n",
    "def load_data_fashion_mnist(batch_size, resize=None):\n",
    "    \"\"\"Download the fashion mnist dataset and then load into memory.\"\"\"\n",
    "    trans = []\n",
    "    if resize:\n",
    "        trans.append(transforms.Resize(size=resize))\n",
    "    trans.append(transforms.ToTensor())\n",
    "    \n",
    "    transform = transforms.Compose(trans)\n",
    "    mnist_train = datasets.FashionMNIST(mode='train', download=True, transform=transform)\n",
    "    mnist_test = datasets.FashionMNIST(mode='test', download=True, transform=transform)\n",
    "    if sys.platform.startswith('win'):\n",
    "        num_workers = 0  # 0表示不用额外的进程来加速读取数据\n",
    "    else:\n",
    "        num_workers = 0\n",
    "    train_iter = paddle.io.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)\n",
    "    test_iter = paddle.io.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)\n",
    "\n",
    "    return train_iter, test_iter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-19T07:36:46.091524Z",
     "start_time": "2019-03-19T07:36:46.067835Z"
    }
   },
   "outputs": [],
   "source": [
    "batch_size = 128\n",
    "# 如出现“out of memory”的报错信息，可减小batch_size或resize\n",
    "train_iter, test_iter = load_data_fashion_mnist(batch_size, resize=224)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5.6.4 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate_accuracy(data_iter, net):\n",
    "\n",
    "    acc_sum, n = 0.0, 0\n",
    "    with paddle.no_grad():\n",
    "        for X, y in data_iter:\n",
    "            if isinstance(net, nn.Layer):\n",
    "                net.eval() # 评估模式, 这会关闭dropout\n",
    "                acc_sum += (net(X).argmax(axis=1) == y.flatten()).astype('float32').sum().numpy()[0]\n",
    "                net.train() # 改回训练模式\n",
    "            else: # 自定义的模型, 3.13节之后不会用到, 不考虑GPU\n",
    "                if('is_training' in net.__code__.co_varnames): # 如果有is_training这个参数\n",
    "                    # 将is_training设置成False\n",
    "                    acc_sum += (net(X, is_training=False).argmax(dim=1) == y).float().sum().item() \n",
    "                else:\n",
    "                    acc_sum += (net(X).argmax(dim=1) == y).float().sum().item() \n",
    "            n += y.shape[0]\n",
    "    return acc_sum / n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train_ch5(net, train_iter, test_iter, batch_size, optimi, num_epochs):\n",
    "\n",
    "    loss = nn.CrossEntropyLoss()\n",
    "    batch_count = 0\n",
    "    for epoch in range(num_epochs):\n",
    "        train_l_sum, train_acc_sum, n, start = 0.0, 0.0, 0, time.time()\n",
    "        for idx, (X, y) in enumerate(train_iter):\n",
    "            y_hat = net(X)\n",
    "            l = loss(y_hat, y)\n",
    "            optimi.clear_grad()\n",
    "            l.backward()\n",
    "            optimi.step()\n",
    "            train_l_sum += l.numpy()[0]\n",
    "            train_acc_sum += (y_hat.argmax(axis=1) == y.flatten()).astype('float32').sum().numpy()[0]\n",
    "            n += y.shape[0]\n",
    "            batch_count += 1\n",
    "        test_acc = evaluate_accuracy(test_iter, net)\n",
    "        print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f, time %.1f sec'\n",
    "              % (epoch + 1, train_l_sum / batch_count, train_acc_sum / n, test_acc, time.time() - start))\n",
    "     "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-03-19T07:36:47.850402Z",
     "start_time": "2019-03-19T07:36:46.092485Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 1, loss 0.6221, train acc 0.794, test acc 0.869, time 66.4 sec\n",
      "epoch 2, loss 0.1631, train acc 0.878, test acc 0.879, time 67.5 sec\n",
      "epoch 3, loss 0.0934, train acc 0.896, test acc 0.894, time 67.2 sec\n",
      "epoch 4, loss 0.0636, train acc 0.906, test acc 0.906, time 71.7 sec\n",
      "epoch 5, loss 0.0476, train acc 0.912, test acc 0.910, time 71.7 sec\n"
     ]
    }
   ],
   "source": [
    "lr, num_epochs = 0.001, 5\n",
    "optimi = optimizer.Adam(learning_rate=lr, parameters=net.parameters())\n",
    "train_ch5(net, train_iter, test_iter, batch_size, optimi, num_epochs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
